#include <iostream>
#include <stdexcept>
#include <string>
#include <signal.h>

#include "grabber.h"
#include "dispatcher.h"
#include "analizer.h"
#include "Configuration.h"
#include "UDPDatagram.h"
#include "RuleCompiler.h"
#include "Rules.h"

using namespace std;

static Grabber grabber;
static Dispatcher *dispatcher = NULL;
static std::vector<Analizer *> analizers;
static bool terminating = false;

void cleanup() {
	cout << "Cleaning up..." << endl;
	
	std::vector<Analizer *>::iterator ait;
	for (ait = analizers.begin(); ait != analizers.end(); ++ait) {
		(*ait)->stop();
		delete *ait;
	}

	if (dispatcher)
		delete dispatcher;
}

void handler(int code) {
	if (terminating)
		return;
	terminating = true;
	grabber.stopLoop();
	cout << endl << "Received SIGINT, terminating..." << endl;
}

int main() {
	cout << "Program started..." << endl;

	/* Read the configuration file */
	Configuration conf;
	if (!conf.loadConfigurationFile("tcpanom.xml")) {
		cerr << "Failed to load configuration file!" << endl;
		return 1;
	}

	RuleCompiler &compiler = RuleCompiler::getInstance();
	std::pair<size_t, size_t> bufSizes =
					compiler.parseFields(conf.getFields());

	std::vector< std::pair<std::string, RootRule *> > anomalies;

	cout << "Compiling rules..." << endl;

	const std::vector<Anomaly> &av = conf.getAnomalies();
	std::vector<Anomaly>::const_iterator it;
	for (it = av.begin(); it != av.end(); ++it) {
		RootRule *r = new RootRule();
		anomalies.emplace_back(it->name, r);
		compiler.compile(*anomalies.back().second, it->operations.front());
	}

	/* Connect SIGINT signal */
	signal(SIGINT, &handler);

	dispatcher = new Dispatcher();

	cout << "Starting analizer threads..." << endl;

	/* Create analizers and connect them to a dispatcher */
	for (int i = 0; i < conf.getThreadCount(); ++i) {
		Analizer *a = new Analizer(conf.getAddress().c_str(),
					conf.getPort(), i, bufSizes.first,
					bufSizes.second, anomalies);
		a->start();
		dispatcher->addAnalizer(a);
		analizers.push_back(a);
	}

	cout << "Starting grabber..." << endl;

	/* Initialize and start packet grabbing */
	try {
		grabber.init(conf.getInterface().c_str(), "tcp");
		grabber.setDispatcher(dispatcher);
		grabber.startLoop();
	}
	catch (runtime_error &err) {
		cerr << err.what() << endl;
		return 1;
	}

	cout << "Grabber stopped." << endl;

	cleanup();

	std::vector< std::pair<std::string, RootRule *> >::iterator anit;
	for (anit = anomalies.begin(); anit != anomalies.end(); ++anit)
		delete anit->second;

	/* When loop breaks, program ends */
	cout << "Done, quitting..." << endl;
	return 0;
}
